package com.jxpanda.infrastructure.crypto.model;

import com.jxpanda.infrastructure.crypto.toolkit.Base64Kit;
import lombok.NonNull;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public record BinaryData(byte[] bytes) {

    /**
     * 从十六进制字符串构造 BinaryData
     *
     * @param hex 每两个字符代表一字节，例如 "0a1b2c"
     */
    public static BinaryData fromHex(String hex) {
        String trimHex = hex.trim();
        if (trimHex.length() % 2 != 0) {
            throw new IllegalArgumentException("Hex string must have even length");
        }
        byte[] buf = new byte[trimHex.length() / 2];
        for (int i = 0; i < buf.length; i++) {
            int idx = i * 2;
            buf[i] = (byte) Integer.parseInt(trimHex.substring(idx, idx + 2), 16);
        }
        return new BinaryData(buf);
    }

    /**
     * 从 Base64 字符串构造 BinaryData
     *
     * @param base64 标准 Base64 编码的字符串
     */
    public static BinaryData fromBase64(String base64) {
        return new BinaryData(Base64Kit.decode(base64));
    }

    /**
     * 便捷地以 UTF-8 解析普通字符串
     */
    public static BinaryData fromString(String text) {
        return fromString(text, StandardCharsets.UTF_8);
    }


    /**
     * 从普通字符串构造 BinaryData
     *
     * @param text    原始文本
     * @param charset 文本对应的字符集
     */
    public static BinaryData fromString(String text, Charset charset) {
        return new BinaryData(text.getBytes(charset));
    }

    public String toHex() {
        return IntStream.range(0, bytes.length)
                .mapToObj(i -> String.format("%02x", bytes[i] & 0xFF))
                .collect(Collectors.joining());
    }

    public String toBase64() {
        return Base64Kit.encodeToString(bytes);
    }

    @NonNull
    @Override
    public String toString() {
        return toString(StandardCharsets.UTF_8);
    }

    public String toString(Charset charset) {
        return new String(bytes, charset);
    }

    public <T> T map(Function<BinaryData, T> mapper) {
        return mapper.apply(this);
    }

}
